#ifndef SDL_GRAPHICS_HPP
#define SDL_GRAPHICS_HPP
#include <SDL3/SDL.h>

#include <algorithm>
#include <cmath>
#include <numbers>
#include <optional>
#include <vector>
#define RETURN_SELF return *this;
namespace gfx {
  float deg_to_rad(float deg) {
    return deg * static_cast<float>(std::numbers::pi) / 180.0f;
  }
  float rad_to_deg(float rad) {
    return rad * 180.0f / static_cast<float>(std::numbers::pi);
  }
  void rotating(SDL_Vertex& v, float angle_deg) {
    float angle_rad = deg_to_rad(angle_deg);
    float cos_theta = std::cos(angle_rad);
    float sin_theta = std::sin(angle_rad);
    float x = v.position.x;
    float y = v.position.y;
    float new_x = x * cos_theta - y * sin_theta;
    float new_y = x * sin_theta + y * cos_theta;
    v.position.x = new_x;
    v.position.y = new_y;
  }
  void rotate_vertices(std::vector<SDL_Vertex>& v, float angle_deg) {
    for (size_t i = 0; i < v.size(); i++) {
      rotating(v[i], angle_deg);
    }
  }
  void rotating(SDL_Vertex& v,
                float angle_deg,
                float cx = 0.0f,
                float cy = 0.0f) {
    float angle_rad = deg_to_rad(angle_deg);
    float cos_theta = std::cos(angle_rad);
    float sin_theta = std::sin(angle_rad);
    float x = v.position.x - cx;
    float y = v.position.y - cy;
    float new_x = x * cos_theta - y * sin_theta;
    float new_y = x * sin_theta + y * cos_theta;
    v.position.x = new_x + cx;
    v.position.y = new_y + cy;
  }
  void rotate_vertices(std::vector<SDL_Vertex>& v,
                       float angle_deg,
                       float cx = 0.0f,
                       float cy = 0.0f) {
    for (size_t i = 0; i < v.size(); i++) {
      rotating(v[i], angle_deg, cx, cy);
    }
  }
  bool point_in_triangle(SDL_FPoint a,
                         SDL_FPoint b,
                         SDL_FPoint c,
                         SDL_FPoint p) {
    auto sign = [](SDL_FPoint p1, SDL_FPoint p2, SDL_FPoint p3) {
      return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
    };
    float d1 = sign(p, a, b);
    float d2 = sign(p, b, c);
    float d3 = sign(p, c, a);
    bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
    bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
    return !(has_neg && has_pos);
  }
  void ensure_counter_clockwise(std::vector<SDL_FPoint>& poly) {
    float area = 0.0f;
    int n = poly.size();
    for (int i = 0; i < n; ++i) {
      int j = (i + 1) % n;
      area += poly[i].x * poly[j].y - poly[j].x * poly[i].y;
    }
    if (area < 0) {
      std::reverse(poly.begin(), poly.end());
    }
  }
  std::vector<int> ear_clipping(const std::vector<SDL_FPoint>& vertices) {
    std::vector<int> indices;
    int n = vertices.size();
    if (n < 3) return indices;

    std::vector<SDL_FPoint> poly = vertices;
    ensure_counter_clockwise(poly);

    std::vector<int> v_list(n);
    for (int i = 0; i < n; ++i) v_list[i] = i;

    int count = n;
    while (count > 3) {
      bool found = false;
      for (int i = 0; i < count; ++i) {
        int prev = (i - 1 + count) % count;
        int curr = i;
        int next = (i + 1) % count;

        SDL_FPoint a = poly[v_list[prev]];
        SDL_FPoint b = poly[v_list[curr]];
        SDL_FPoint c = poly[v_list[next]];
        if ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) <= 0)
          continue;
        bool has_inside = false;
        for (int k = 0; k < count; ++k) {
          if (k == prev || k == curr || k == next) continue;
          if (point_in_triangle(a, b, c, poly[v_list[k]])) {
            has_inside = true;
            break;
          }
        }
        if (has_inside) continue;
        indices.push_back(v_list[prev]);
        indices.push_back(v_list[curr]);
        indices.push_back(v_list[next]);
        v_list.erase(v_list.begin() + curr);
        count--;
        found = true;
        break;
      }
      if (!found) break;
    }
    if (count == 3) {
      indices.push_back(v_list[0]);
      indices.push_back(v_list[1]);
      indices.push_back(v_list[2]);
    }
    return indices;
  }
  inline float binomial_coefficient(int n, int k) {
    if (k < 0 || k > n) return 0.0f;
    if (k == 0 || k == n) return 1.0f;
    float coeff = 1.0f;
    k = std::min(k, n - k);
    for (int i = 1; i <= k; ++i) {
      coeff = coeff * (n - k + i) / i;
    }
    return coeff;
  }
  SDL_FPoint bezier_point(const std::vector<SDL_FPoint>& control_points,
                          float t) {
    const int n = control_points.size() - 1;
    SDL_FPoint result = {0.0f, 0.0f};
    if (n == 0) return control_points[0];
    if (t <= 0.0f) return control_points[0];
    if (t >= 1.0f) return control_points[n];
    float t_k = 1.0f;
    float inv_t_nk = std::pow(1.0f - t, n);
    for (int k = 0; k <= n; ++k) {
      const float C = binomial_coefficient(n, k);
      const float bernstein = C * t_k * inv_t_nk;
      result.x += bernstein * control_points[k].x;
      result.y += bernstein * control_points[k].y;
      t_k *= t;
      inv_t_nk /= (1.0f - t);
    }

    return result;
  }

  SDL_FPoint calculate_center(const std::vector<SDL_FPoint>& points) {
    SDL_FPoint center = {0, 0};
    for (const auto& p : points) {
      center.x += p.x;
      center.y += p.y;
    }
    center.x /= static_cast<float>(points.size());
    center.y /= static_cast<float>(points.size());
    return center;
  }
  enum class render_precision {
    very_low = 8,
    low = 16,
    medium = 32,
    high = 64,
    very_high = 128
  };
  class renderer {
   public:
    // 所有精度都为顶点数
    enum class angle_unit { degrees, radians };

    renderer(SDL_Renderer* bind) : bind_r(bind) {}

    // 基本图形
    renderer& circle(SDL_FPoint center, float radius) {
      ellipse(center, {radius, radius});
      RETURN_SELF
    }
    renderer& fill_circle(SDL_FPoint center, float radius) {
      fill_ellipse(center, {radius, radius});
      RETURN_SELF
    }
    renderer& ellipse(SDL_FPoint center, SDL_FPoint xy) {
      precision = std::max(precision, 3);
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_Vertex> vertices;
      vertices.reserve(precision * 2);
      float rotation_deg = 0.0f;
      const bool need_rotation = render_angle.has_value();
      if (need_rotation) {
        rotation_deg = (ang_unit == angle_unit::degrees)
                           ? render_angle.value()
                           : rad_to_deg(render_angle.value());
      }
      const float step = 2.0f * static_cast<float>(std::numbers::pi) /
                         static_cast<float>(precision);
      const float outer_x = xy.x;
      const float outer_y = xy.y;
      const float inner_x = std::max(0.0f, outer_x - wireframe_width);
      const float inner_y = std::max(0.0f, outer_y - wireframe_width);
      for (int i = 0; i < precision; ++i) {
        const float theta = static_cast<float>(i) * step;
        SDL_Vertex vertex = {.position = {center.x + outer_x * std::cos(theta),
                                          center.y + outer_y * std::sin(theta)},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (need_rotation) {
          rotating(vertex, rotation_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      for (int i = 0; i < precision; ++i) {
        const float theta = static_cast<float>(i) * step;
        SDL_Vertex vertex = {.position = {center.x + inner_x * std::cos(theta),
                                          center.y + inner_y * std::sin(theta)},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (need_rotation) {
          rotating(vertex, rotation_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      std::vector<int> indices;
      indices.reserve(precision * 6);
      for (int i = 0; i < precision; ++i) {
        const int j = (i + 1) % precision;
        const int outer_i = i;
        const int outer_j = j;
        const int inner_i = i + precision;
        const int inner_j = j + precision;
        indices.push_back(outer_i);
        indices.push_back(inner_i);
        indices.push_back(outer_j);
        indices.push_back(inner_i);
        indices.push_back(inner_j);
        indices.push_back(outer_j);
      }
      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    renderer& fill_ellipse(SDL_FPoint center, SDL_FPoint xy) {
      precision = std::max(precision, 3);
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_Vertex> vertices;
      vertices.reserve(precision + 1);
      vertices.push_back({.position = center,
                          .color = current_color,
                          .tex_coord = {0.0f, 0.0f}});
      float rotation_deg = 0.0f;
      const bool need_rotation = render_angle.has_value();
      if (need_rotation) {
        if (ang_unit == angle_unit::degrees) {
          rotation_deg = render_angle.value();
        } else {
          rotation_deg = rad_to_deg(render_angle.value());
        }
      }

      const float step = 2.0f * static_cast<float>(std::numbers::pi) /
                         static_cast<float>(precision);
      for (int i = 0; i < precision; ++i) {
        const float theta = static_cast<float>(i) * step;
        float x = center.x + xy.x * std::cos(theta);
        float y = center.y + xy.y * std::sin(theta);
        SDL_Vertex vertex = {.position = {x, y},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (need_rotation) {
          rotating(vertex, rotation_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      std::vector<int> indices;
      indices.reserve(precision * 3);
      for (int i = 1; i <= precision; ++i) {
        indices.push_back(0);
        indices.push_back(i);
        indices.push_back((i % precision) + 1);
      }
      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    renderer& rect(SDL_FRect draw_rect) {
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);
      SDL_FPoint center = {draw_rect.x + draw_rect.w / 2.0f,
                           draw_rect.y + draw_rect.h / 2.0f};
      const float half_w = draw_rect.w / 2.0f;
      const float half_h = draw_rect.h / 2.0f;
      std::vector<SDL_Vertex> outer_verts = {
          {{center.x - half_w, center.y - half_h}, current_color, {0, 0}},
          {{center.x + half_w, center.y - half_h}, current_color, {0, 0}},
          {{center.x + half_w, center.y + half_h}, current_color, {0, 0}},
          {{center.x - half_w, center.y + half_h}, current_color, {0, 0}}};
      const float inner_half_w = std::max(0.0f, half_w - wireframe_width);
      const float inner_half_h = std::max(0.0f, half_h - wireframe_width);
      std::vector<SDL_Vertex> inner_verts = {
          {{center.x - inner_half_w, center.y - inner_half_h},
           current_color,
           {0, 0}},
          {{center.x + inner_half_w, center.y - inner_half_h},
           current_color,
           {0, 0}},
          {{center.x + inner_half_w, center.y + inner_half_h},
           current_color,
           {0, 0}},
          {{center.x - inner_half_w, center.y + inner_half_h},
           current_color,
           {0, 0}}};
      float rotation_deg = 0.0f;
      const bool need_rotation = render_angle.has_value();
      if (need_rotation) {
        rotation_deg = (ang_unit == angle_unit::degrees)
                           ? render_angle.value()
                           : rad_to_deg(render_angle.value());
        for (auto& vert : outer_verts)
          rotating(vert, rotation_deg, center.x, center.y);
        for (auto& vert : inner_verts)
          rotating(vert, rotation_deg, center.x, center.y);
      }
      std::vector<SDL_Vertex> vertices = outer_verts;
      vertices.insert(vertices.end(), inner_verts.begin(), inner_verts.end());
      std::vector<int> indices = {0, 4, 1, 4, 5, 1, 1, 5, 2, 5, 6, 2,
                                  2, 6, 3, 6, 7, 3, 3, 7, 0, 7, 4, 0};
      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());
      RETURN_SELF
    }
    renderer& fill_rect(SDL_FRect draw_rect) {
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);
      SDL_FPoint center = {draw_rect.x + draw_rect.w / 2.0f,
                           draw_rect.y + draw_rect.h / 2.0f};
      const float half_w = draw_rect.w / 2.0f;
      const float half_h = draw_rect.h / 2.0f;
      std::vector<SDL_Vertex> vertices = {
          {{center.x - half_w, center.y - half_h}, current_color, {0, 0}},
          {{center.x + half_w, center.y - half_h}, current_color, {0, 0}},
          {{center.x + half_w, center.y + half_h}, current_color, {0, 0}},
          {{center.x - half_w, center.y + half_h}, current_color, {0, 0}}};
      float rotation_deg = 0.0f;
      const bool need_rotation = render_angle.has_value();
      if (need_rotation) {
        rotation_deg = (ang_unit == angle_unit::degrees)
                           ? render_angle.value()
                           : rad_to_deg(render_angle.value());
        for (auto& vert : vertices)
          rotating(vert, rotation_deg, center.x, center.y);
      }
      std::vector<int> indices = {0, 1, 2, 0, 2, 3};
      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());
      RETURN_SELF
    }
    renderer& fill_rounded_rect(SDL_FRect draw_rect, float radius) {
      precision = std::max(precision, 12);
      int points_per_corner = std::max(3, precision / 4);

      SDL_FPoint center = {draw_rect.x + draw_rect.w / 2.0f,
                           draw_rect.y + draw_rect.h / 2.0f};
      float half_w = draw_rect.w / 2.0f;
      float half_h = draw_rect.h / 2.0f;

      radius = std::clamp(radius, 0.0f, std::min(half_w, half_h));
      if (radius <= 0.0f) return fill_rect(draw_rect);

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_Vertex> vertices;
      const float angle_step =
          (std::numbers::pi / 2.0f) / (points_per_corner - 1);
      SDL_FPoint tl_center = {center.x - half_w + radius,
                              center.y - half_h + radius};
      for (int i = 0; i < points_per_corner; ++i) {
        float angle = std::numbers::pi + i * angle_step;
        vertices.push_back({{tl_center.x + radius * std::cos(angle),
                             tl_center.y + radius * std::sin(angle)},
                            current_color,
                            {0, 0}});
      }

      SDL_FPoint tr_center = {center.x + half_w - radius,
                              center.y - half_h + radius};
      for (int i = 0; i < points_per_corner; ++i) {
        float angle = 3 * std::numbers::pi / 2.0f + i * angle_step;
        vertices.push_back({{tr_center.x + radius * std::cos(angle),
                             tr_center.y + radius * std::sin(angle)},
                            current_color,
                            {0, 0}});
      }

      SDL_FPoint br_center = {center.x + half_w - radius,
                              center.y + half_h - radius};
      for (int i = 0; i < points_per_corner; ++i) {
        float angle = i * angle_step;
        vertices.push_back({{br_center.x + radius * std::cos(angle),
                             br_center.y + radius * std::sin(angle)},
                            current_color,
                            {0, 0}});
      }

      SDL_FPoint bl_center = {center.x - half_w + radius,
                              center.y + half_h - radius};
      for (int i = 0; i < points_per_corner; ++i) {
        float angle = std::numbers::pi / 2.0f + i * angle_step;
        vertices.push_back({{bl_center.x + radius * std::cos(angle),
                             bl_center.y + radius * std::sin(angle)},
                            current_color,
                            {0, 0}});
      }

      if (render_angle.has_value()) {
        float rot_deg = (ang_unit == angle_unit::degrees)
                            ? render_angle.value()
                            : rad_to_deg(render_angle.value());
        rotate_vertices(vertices, rot_deg, center.x, center.y);
      }

      std::vector<int> indices;
      for (int i = 1; i < (int)vertices.size() - 1; ++i) {
        indices.insert(indices.end(), {0, i, i + 1});
      }

      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());
      RETURN_SELF
    }

    renderer& rounded_rect(SDL_FRect draw_rect, float radius) {
      precision = std::max(precision, 12);
      int points_per_corner = std::max(3, precision / 4);

      SDL_FPoint center = {draw_rect.x + draw_rect.w / 2.0f,
                           draw_rect.y + draw_rect.h / 2.0f};
      float half_w = draw_rect.w / 2.0f;
      float half_h = draw_rect.h / 2.0f;

      float outer_radius = std::clamp(radius, 0.0f, std::min(half_w, half_h));
      if (outer_radius <= 0.0f) return rect(draw_rect);

      float inner_half_w = std::max(0.0f, half_w - wireframe_width);
      float inner_half_h = std::max(0.0f, half_h - wireframe_width);
      float inner_radius = std::clamp(
          radius - wireframe_width, 0.0f, std::min(inner_half_w, inner_half_h));

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_Vertex> outer_verts, inner_verts;
      const float angle_step =
          (std::numbers::pi / 2.0f) / (points_per_corner - 1);

      auto generate_outer = [&](SDL_FPoint c, float start_angle) {
        for (int i = 0; i < points_per_corner; ++i) {
          float angle = start_angle + i * angle_step;
          outer_verts.push_back({{c.x + outer_radius * std::cos(angle),
                                  c.y + outer_radius * std::sin(angle)},
                                 current_color,
                                 {0, 0}});
        }
      };
      generate_outer(
          {center.x - half_w + outer_radius, center.y - half_h + outer_radius},
          std::numbers::pi);
      generate_outer(
          {center.x + half_w - outer_radius, center.y - half_h + outer_radius},
          3 * std::numbers::pi / 2);
      generate_outer(
          {center.x + half_w - outer_radius, center.y + half_h - outer_radius},
          0);
      generate_outer(
          {center.x - half_w + outer_radius, center.y + half_h - outer_radius},
          std::numbers::pi / 2);

      auto generate_inner = [&](SDL_FPoint c, float start_angle) {
        for (int i = 0; i < points_per_corner; ++i) {
          float angle = start_angle + i * angle_step;
          inner_verts.push_back({{c.x + inner_radius * std::cos(angle),
                                  c.y + inner_radius * std::sin(angle)},
                                 current_color,
                                 {0, 0}});
        }
      };
      generate_inner({center.x - inner_half_w + inner_radius,
                      center.y - inner_half_h + inner_radius},
                     std::numbers::pi);
      generate_inner({center.x + inner_half_w - inner_radius,
                      center.y - inner_half_h + inner_radius},
                     3 * std::numbers::pi / 2);
      generate_inner({center.x + inner_half_w - inner_radius,
                      center.y + inner_half_h - inner_radius},
                     0);
      generate_inner({center.x - inner_half_w + inner_radius,
                      center.y + inner_half_h - inner_radius},
                     std::numbers::pi / 2);

      if (render_angle.has_value()) {
        float rot_deg = (ang_unit == angle_unit::degrees)
                            ? render_angle.value()
                            : rad_to_deg(render_angle.value());
        rotate_vertices(outer_verts, rot_deg, center.x, center.y);
        rotate_vertices(inner_verts, rot_deg, center.x, center.y);
      }

      std::vector<SDL_Vertex> vertices = outer_verts;
      vertices.insert(vertices.end(), inner_verts.begin(), inner_verts.end());
      const int num_outer = outer_verts.size();

      std::vector<int> indices;
      for (int i = 0; i < num_outer; ++i) {
        int j = (i + 1) % num_outer;
        indices.insert(indices.end(),
                       {i, i + num_outer, j, i + num_outer, j + num_outer, j});
      }

      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());
      RETURN_SELF
    }

    // 弧形和扇形
    renderer& arc(SDL_FPoint center,
                  float radius,
                  float start_angle,
                  float end_angle) {
      elliptical_arc(center, {radius, radius}, start_angle, end_angle);
      RETURN_SELF
    }
    renderer& elliptical_arc(SDL_FPoint center,
                             SDL_FPoint xy,
                             float start_angle,
                             float end_angle) {
      precision = std::max(precision, 3);
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      float start_rad, end_rad;
      if (ang_unit == angle_unit::degrees) {
        start_rad = deg_to_rad(start_angle);
        end_rad = deg_to_rad(end_angle);
      } else {
        start_rad = start_angle;
        end_rad = end_angle;
      }
      float angle_diff = end_rad - start_rad;
      if (angle_diff <= 0) {
        angle_diff += 2.0f * static_cast<float>(std::numbers::pi);
      }
      const float step = angle_diff / static_cast<float>(precision);

      std::vector<SDL_Vertex> vertices;
      vertices.reserve(precision * 2);
      const float outer_x = xy.x;
      const float outer_y = xy.y;
      const float inner_x = std::max(0.0f, outer_x - wireframe_width);
      const float inner_y = std::max(0.0f, outer_y - wireframe_width);
      for (int i = 0; i <= precision; ++i) {
        const float theta = start_rad + static_cast<float>(i) * step;
        SDL_Vertex vertex = {.position = {center.x + outer_x * std::cos(theta),
                                          center.y + outer_y * std::sin(theta)},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (render_angle.has_value()) {
          float rot_deg = (ang_unit == angle_unit::degrees)
                              ? render_angle.value()
                              : rad_to_deg(render_angle.value());
          rotating(vertex, rot_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      for (int i = precision; i >= 0; --i) {
        const float theta = start_rad + static_cast<float>(i) * step;
        SDL_Vertex vertex = {.position = {center.x + inner_x * std::cos(theta),
                                          center.y + inner_y * std::sin(theta)},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (render_angle.has_value()) {
          float rot_deg = (ang_unit == angle_unit::degrees)
                              ? render_angle.value()
                              : rad_to_deg(render_angle.value());
          rotating(vertex, rot_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      std::vector<int> indices;
      indices.reserve(precision * 6);
      const int outer_count = precision + 1;
      for (int i = 0; i < precision; ++i) {
        const int outer_i = i;
        const int outer_j = i + 1;
        const int inner_i = outer_count + (outer_count - 1 - i);
        const int inner_j = outer_count + (outer_count - 1 - (i + 1));

        indices.push_back(outer_i);
        indices.push_back(inner_i);
        indices.push_back(outer_j);
        indices.push_back(inner_i);
        indices.push_back(inner_j);
        indices.push_back(outer_j);
      }

      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    renderer& sector(SDL_FPoint center,
                     float radius,
                     float start_angle,
                     float end_angle) {
      elliptical_sector(center, {radius, radius}, start_angle, end_angle);
      RETURN_SELF
    }
    renderer& elliptical_sector(SDL_FPoint center,
                                SDL_FPoint xy,
                                float start_angle,
                                float end_angle) {
      precision = std::max(precision, 3);
      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      float start_rad, end_rad;
      if (ang_unit == angle_unit::degrees) {
        start_rad = deg_to_rad(start_angle);
        end_rad = deg_to_rad(end_angle);
      } else {
        start_rad = start_angle;
        end_rad = end_angle;
      }

      float angle_diff = end_rad - start_rad;
      if (angle_diff <= 0) {
        angle_diff += 2.0f * static_cast<float>(std::numbers::pi);
      }
      const float step = angle_diff / static_cast<float>(precision);

      std::vector<SDL_Vertex> vertices;
      vertices.reserve(precision + 2);
      vertices.push_back({.position = center,
                          .color = current_color,
                          .tex_coord = {0.0f, 0.0f}});
      for (int i = 0; i <= precision; ++i) {
        const float theta = start_rad + static_cast<float>(i) * step;
        SDL_Vertex vertex = {.position = {center.x + xy.x * std::cos(theta),
                                          center.y + xy.y * std::sin(theta)},
                             .color = current_color,
                             .tex_coord = {0.0f, 0.0f}};
        if (render_angle.has_value()) {
          float rot_deg = (ang_unit == angle_unit::degrees)
                              ? render_angle.value()
                              : rad_to_deg(render_angle.value());
          rotating(vertex, rot_deg, center.x, center.y);
        }
        vertices.push_back(vertex);
      }
      std::vector<int> indices;
      indices.reserve(precision * 3);
      for (int i = 1; i <= precision; ++i) {
        indices.push_back(0);
        indices.push_back(i);
        indices.push_back(i + 1);
      }

      SDL_RenderGeometry(bind_r,
                         nullptr,
                         vertices.data(),
                         vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    renderer& polygon(const std::vector<SDL_FPoint>& vertices) {
      int count = vertices.size();
      auto vertex = vertices.data();
      if (count < 3) RETURN_SELF;
      if (wireframe_width <= 0) RETURN_SELF;

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      SDL_FPoint center = {0, 0};
      for (int i = 0; i < count; ++i) {
        center.x += vertex[i].x;
        center.y += vertex[i].y;
      }
      center.x /= count;
      center.y /= count;

      std::vector<SDL_FPoint> outer_verts(vertex, vertex + count);
      std::vector<SDL_FPoint> inner_verts(count);

      for (int i = 0; i < count; ++i) {
        const int prev = (i - 1 + count) % count;
        const int next = (i + 1) % count;
        const SDL_FPoint e1 = {outer_verts[i].x - outer_verts[prev].x,
                               outer_verts[i].y - outer_verts[prev].y};
        SDL_FPoint n1 = {-e1.y, e1.x};
        const float len1 = std::hypot(n1.x, n1.y);
        if (len1 > 0) {
          n1.x /= len1;
          n1.y /= len1;
        }

        const SDL_FPoint e2 = {outer_verts[next].x - outer_verts[i].x,
                               outer_verts[next].y - outer_verts[i].y};
        SDL_FPoint n2 = {-e2.y, e2.x};
        const float len2 = std::hypot(n2.x, n2.y);
        if (len2 > 0) {
          n2.x /= len2;
          n2.y /= len2;
        }

        SDL_FPoint avg_norm = {n1.x + n2.x, n1.y + n2.y};
        const float avg_len = std::hypot(avg_norm.x, avg_norm.y);
        if (avg_len > 0) {
          avg_norm.x /= avg_len;
          avg_norm.y /= avg_len;
        }

        const float offset = wireframe_width / 2.0f;
        inner_verts[i].x = outer_verts[i].x - avg_norm.x * offset;
        inner_verts[i].y = outer_verts[i].y - avg_norm.y * offset;
      }

      std::vector<SDL_Vertex> Vertices;
      Vertices.reserve(count * 2);
      const bool need_rotation = render_angle.has_value();
      const float rotation_deg = need_rotation
                                     ? (ang_unit == angle_unit::degrees
                                            ? render_angle.value()
                                            : rad_to_deg(render_angle.value()))
                                     : 0.0f;

      for (int i = 0; i < count; ++i) {
        SDL_Vertex v = {outer_verts[i], current_color, {0, 0}};
        if (need_rotation) rotating(v, rotation_deg, center.x, center.y);
        Vertices.push_back(v);
      }

      for (int i = 0; i < count; ++i) {
        SDL_Vertex v = {inner_verts[i], current_color, {0, 0}};
        if (need_rotation) rotating(v, rotation_deg, center.x, center.y);
        Vertices.push_back(v);
      }

      std::vector<int> indices;
      indices.reserve(count * 6);
      for (int i = 0; i < count; ++i) {
        const int j = (i + 1) % count;
        const int outer_i = i;
        const int outer_j = j;
        const int inner_i = i + count;
        const int inner_j = j + count;

        indices.insert(indices.end(),
                       {outer_i, inner_i, outer_j, inner_i, inner_j, outer_j});
      }

      SDL_RenderGeometry(bind_r,
                         nullptr,
                         Vertices.data(),
                         Vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    renderer& fill_polygon(const std::vector<SDL_FPoint>& vertices) {
      if (vertices.size() < 3) RETURN_SELF;

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_FPoint> poly = vertices;
      ensure_counter_clockwise(poly);
      SDL_FPoint center = {0, 0};
      for (const auto& p : poly) {
        center.x += p.x;
        center.y += p.y;
      }
      center.x /= (float)poly.size();
      center.y /= (float)poly.size();

      std::vector<SDL_Vertex> verts;
      verts.reserve(poly.size());
      float rot_deg = 0.0f;
      const bool need_rotation = render_angle.has_value();
      if (need_rotation) {
        rot_deg = (ang_unit == angle_unit::degrees)
                      ? render_angle.value()
                      : rad_to_deg(render_angle.value());
      }
      for (const auto& p : poly) {
        SDL_Vertex v;
        v.position = p;
        v.color = current_color;
        v.tex_coord = {0, 0};
        if (need_rotation) {
          rotating(v, rot_deg, center.x, center.y);
        }
        verts.push_back(v);
      }
      std::vector<int> indices = ear_clipping(poly);
      if (!indices.empty()) {
        SDL_RenderGeometry(bind_r,
                           nullptr,
                           verts.data(),
                           verts.size(),
                           indices.data(),
                           indices.size());
      }

      RETURN_SELF
    }
    template <typename... Args>
    renderer& bezier(Args&&... control_points) {
      std::vector<SDL_FPoint> controls = {
          std::forward<Args>(control_points)...};
      SDL_FPoint center = calculate_center(controls);

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      std::vector<SDL_FPoint> curve_points;
      std::vector<SDL_FPoint> tangents;
      int sample_precision = std::max(precision, 32);
      float step = 1.0f / sample_precision;

      for (int i = 0; i <= sample_precision; ++i) {
        float t = i * step;
        curve_points.push_back(bezier_point(controls, t));
        tangents.push_back(calculate_bezier_tangent(controls, t));
      }
      std::vector<SDL_Vertex> strip_vertices;
      float half_width = wireframe_width / 2.0f;

      for (int i = 0; i < (int)curve_points.size(); ++i) {
        const auto& p = curve_points[i];
        const auto& tangent = tangents[i];
        SDL_FPoint normal = {-tangent.y, tangent.x};
        SDL_Vertex upper = {
            {p.x + normal.x * half_width, p.y + normal.y * half_width},
            current_color,
            {0, 0}};
        SDL_Vertex lower = {
            {p.x - normal.x * half_width, p.y - normal.y * half_width},
            current_color,
            {0, 0}};
        if (render_angle.has_value()) {
          float rot_deg = (ang_unit == angle_unit::degrees)
                              ? render_angle.value()
                              : rad_to_deg(render_angle.value());
          rotating(upper, rot_deg, center.x, center.y);
          rotating(lower, rot_deg, center.x, center.y);
        }

        strip_vertices.push_back(upper);
        strip_vertices.push_back(lower);
      }
      std::vector<int> indices;
      for (int i = 0; i < (int)strip_vertices.size() - 2; i += 2) {
        indices.insert(indices.end(), {i, i + 1, i + 2, i + 2, i + 1, i + 3});
      }
      if (std::hypot(curve_points.front().x - curve_points.back().x,
                     curve_points.front().y - curve_points.back().y) < 1.0f) {
        indices.insert(indices.end(),
                       {(int)strip_vertices.size() - 2,
                        (int)strip_vertices.size() - 1,
                        0,
                        0,
                        (int)strip_vertices.size() - 1,
                        1});
      }
      SDL_RenderGeometry(bind_r,
                         nullptr,
                         strip_vertices.data(),
                         strip_vertices.size(),
                         indices.data(),
                         indices.size());

      RETURN_SELF
    }
    SDL_FPoint calculate_bezier_tangent(const std::vector<SDL_FPoint>& controls,
                                        float t) {
      int n = controls.size() - 1;
      SDL_FPoint tangent = {0, 0};
      for (int k = 0; k < n; ++k) {
        float C = binomial_coefficient(n - 1, k);
        float t_pow = std::pow(t, k);
        float inv_t_pow = std::pow(1 - t, n - 1 - k);
        float bernstein = n * C * t_pow * inv_t_pow;

        tangent.x += bernstein * (controls[k + 1].x - controls[k].x);
        tangent.y += bernstein * (controls[k + 1].y - controls[k].y);
      }
      float len = std::hypot(tangent.x, tangent.y);
      if (len < 1e-6) return {1, 0};
      tangent.x /= len;
      tangent.y /= len;
      return tangent;
    }
    template <typename... Args>
    renderer& fill_bezier(Args&&... control_points) {
      static_assert(sizeof...(Args) >= 3, "填充贝塞尔曲线至少需要3个控制点");

      std::vector<SDL_FPoint> controls = {
          std::forward<Args>(control_points)...};
      SDL_FPoint center = calculate_center(controls);

      SDL_FColor current_color;
      SDL_GetRenderDrawColorFloat(bind_r,
                                  &current_color.r,
                                  &current_color.g,
                                  &current_color.b,
                                  &current_color.a);

      int fill_precision = std::max(precision, 64);
      std::vector<SDL_FPoint> curve_points;
      curve_points.reserve(fill_precision + 1);

      const float step = 1.0f / static_cast<float>(fill_precision);
      for (int i = 0; i <= fill_precision; ++i) {
        float t = static_cast<float>(i) * step;
        curve_points.push_back(bezier_point(controls, t));
      }

      std::vector<SDL_Vertex> vertices;
      vertices.reserve(fill_precision + 1);
      if (render_angle.has_value()) {
        float rot_deg = (ang_unit == angle_unit::degrees)
                            ? render_angle.value()
                            : rad_to_deg(render_angle.value());
        for (const auto& p : curve_points) {
          SDL_Vertex v = {p, current_color, {0, 0}};
          rotating(v, rot_deg, center.x, center.y);
          vertices.push_back(v);
        }
      } else {
        for (const auto& p : curve_points) {
          vertices.push_back({p, current_color, {0, 0}});
        }
      }

      std::vector<int> indices = ear_clipping(curve_points);
      if (!indices.empty()) {
        SDL_RenderGeometry(bind_r,
                           nullptr,
                           vertices.data(),
                           vertices.size(),
                           indices.data(),
                           indices.size());
      }

      RETURN_SELF
    }
    renderer& set_render_angle(std::optional<float> angle) {
      render_angle = angle;
      RETURN_SELF
    }
    renderer& set_angle_unit(angle_unit unit = angle_unit::degrees) {
      ang_unit = unit;
      RETURN_SELF
    }
    renderer& set_wireframe_width(float width = 1.0f) {
      wireframe_width = width;
      RETURN_SELF
    }
    renderer& set_render_precision(int p = 64) {
      precision = p;
      RETURN_SELF
    }
    renderer& set_color(SDL_Color color) {
      this->set_color(color.r, color.g, color.b, color.a);
      RETURN_SELF
    }
    renderer& set_color(SDL_FColor color) {
      this->set_color(color.r, color.g, color.b, color.a);
      RETURN_SELF
    }
    renderer& set_color(int r, int g, int b, int a = 255) {
      SDL_SetRenderDrawColor(bind_r, r, g, b, a);
      RETURN_SELF
    }
    renderer& set_color(float r, float g, float b, float a = 255) {
      SDL_SetRenderDrawColorFloat(bind_r, r, g, b, a);
      RETURN_SELF
    }

   private:
    SDL_Renderer* bind_r = nullptr;
    std::optional<float> render_angle = std::nullopt;
    angle_unit ang_unit = angle_unit::degrees;
    float wireframe_width = 1;
    int precision = 32;
  };
}  // namespace gfx
#undef RETURN_SELF
#endif  // sdl_gfx.hpp